Group features topic

ExpansionTileItems can be seamlessly grouped and their interactions easily managed, offering you unparalleled control and flexibility.

Check out Example

Get Started

To group and manage ExpansionTileItem, there are 2 options to do that:

  • Option 1: Wrap them inside ExpansionTileGroup widget.
  • Option 2: Using ExansionGroupController

Behaviors between items in the group

Before we start you should to know the type of behaviors between items in group. We defined an enum to manage them ToggleType. This enum contains almost common case you will be met:

toggleType type Description
none It's default. Do nothing if an item in group changed behavior.
expandOnlyCurrent When an item in group is expanded, would collapse all the others.
collapseAll Collapse all items if any item in group is collapsed.
expandAll Expanded all items if any item in group is expanded.
expandAllOrCollapseAll Expanded/Collapsed all items if any item in group is Expanded/Collapsed.
expandAlwaysCurrent Expand the tapped item and collapse all others, but still remain expanding when tapping this item again.
ToggleType.expandOnlyCurrent
Image
ToggleType.collapseAll
Image
ToggleType.expandAll
Image
ToggleType.expandAllOrCollapseAll
Image

Let implement with Option 1: Wrap them inside ExpansionTileGroup widget.

You have a list of ExpansionTileItem and want to group them, just simply wrap them into ExpansionTileGroup

Fully code example:

class ExpansionGroupExample extends StatelessWidget {
 const ExpansionGroupExample({Key? key}) : super(key: key);
 @override
 Widget build(BuildContext context) {
   return ExpansionTileGroup(
       toggleType: ToggleType.expandOnlyCurrent,
       onExpansionItemChanged: (index, isExpanded) {
         //index: the position of item that just changed state,
         //isExpanded: present current behavior:
         //- true: the item is expanding,
         //- false: the item is collapsing
       },
       spaceBetweenItem: 16,
       children: [
         const ExpansionTileItem(
           title: Text('ExpansionTile item 1'),
           expendedBorderColor: Colors.blue,
           children: [
             Text('Title of expansion tile item 1'),
           ],
         ),
         const ExpansionTileOutlined(
           title: Text('ExpansionTile item 2'),
           expendedBorderColor: Colors.blue,
           children: [
             Text('Title of expansion tile item 2'),
           ],
         ),
         const ExpansionTileFlat(
           title: Text('ExpansionTile item 3'),
           expendedBorderColor: Colors.blue,
           children: [
             Text('Title of expansion tile item 3'),
           ],
         ),
       ]
   );
 }
}

That's it!

ExpansionTileGroup parameters

Parameter Description
key Controls how one widget replaces another widget in the tree.
children* The children in this group, ExpansionTileItem
toggleType Provide the behaviors of items in this group, it's enum
spaceBetweenItem The gap space between item in the group
onExpansionItemChanged Listen the changed behavior of any item in the group

Let implement with Option 2: Using ExansionGroupController

Why do we have option 2 while option 1 work well?

Well, the Option 1 only works with List of ExpansionTileItem.

In a case, you have a list of Widget and each widget contains one or many ExpansionTileItem inside. That the Option 1 can't handle this case.

So we have the Option 2 by using ExansionGroupController

Step by Step:

Step 1: Define ExpansionGroupController

controller = ExpansionGroupController(
  length: 6, toggleType: ToggleType.expandAllOrCollapseAll);

The lenth is required.

The toggleType as mentioned above. Default is ToggleType.none

Step 2: Pass controller and index in each ExpansionTileItem

ExpansionTileOutlined(
                controller: controller,
                index: 0,
                title: Text('HELLO, I\'m index: 0'),
                children: [])

Make sure the index is always less than the length because index always counted from 0.

To list itemChanged in group just add this listener;

controller.addItemChangedListener(
      (index, isExpanded) {
        debugPrint('index: $index, isExpanded: $isExpanded');
      },
    );

Finally, don't forget to dispose the controller

controller.dispose();

Below example code shows you a List of ExpansionTileItem wrapped by a ListView and a ExpansionTileItem wrapped by a Container.

import 'package:expansion_tile_group/expansion_tile_group.dart';
import 'package:flutter/material.dart';

class CustomGroupWithController extends StatefulWidget {
  const CustomGroupWithController({Key? key}) : super(key: key);

  @override
  State<CustomGroupWithController> createState() =>
      _CustomGroupWithControllerState();
}

class _CustomGroupWithControllerState extends State<CustomGroupWithController> {
  late final ExpansionGroupController controller;

  @override
  void initState() {
    controller = ExpansionGroupController(
        length: 6, initialIndex: 0, toggleType: ToggleType.expandAllOrCollapseAll);

    controller.addItemChangedListener(
      (index, isExpanded) {
        debugPrint('index: $index, isExpanded: $isExpanded');
      },
    );
    super.initState();
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ListView.separated(
            separatorBuilder: (context, index) => const SizedBox(
              height: 8,
            ),
            shrinkWrap: true,
            itemBuilder: (context, index) => ExpansionTileOutlined(
                controller: controller,
                index: index,
                title: Text('HELLO, I\'m index: $index of ListView'),
                children: [sampleContent]),
            itemCount: 5),
        Container(
          color: Colors.yellow,
          width: 250,
          child: ExpansionTileFlat(
              controller: controller,
              index: 5,
              title:
              const Text('HELLO, I\'m index: 5 and wrap by Container'),
              children: [sampleContent]),
        )
      ],
    );
  }

  Widget get sampleContent => Material(
        child: InkWell(
          onTap: () {},
          child: const Text(
              ''' Nullam eleifend ultrices tortor, sit amet gravida sapien cursus vitae. Duis rutrum convallis erat et ultrices. Morbi a luctus ligula, at varius ligula. Nam mollis sapien ac nunc hendrerit consequat. Cras posuere metus felis, at pellentesque sem ornare id. Praesent ut nunc aliquam, dictum felis eu, congue metus. Nunc vitae elit eros. In eu dui pharetra, varius metus a, efficitur eros.'''),
        ),
      );
}

That's it! Hope you work well!

Libraries

expansion_tile_group Get started Upgrading Item features Group features Troubleshooting
This package overcomes the limitations of the standard ExpansionTile widget by incorporating highly customizable widgets that fully extends its functionality. These widgets can be seamlessly grouped and their interactions easily managed, offering you unparalleled control and flexibility.